package org.appfuse.webapp.action;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.sf.navigator.menu.MenuComponent;
import net.sf.navigator.menu.MenuRepository;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.struts.Globals;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionMessage;
import org.apache.struts.action.ActionMessages;
import org.apache.struts.config.ActionConfig;

import org.appfuse.webapp.action.BaseAction;
import org.appfuse.Constants;
import org.appfuse.model.Criteria;
import org.appfuse.model.Menu;
import org.appfuse.service.CriteriaExistsException;
import org.appfuse.service.CriteriaManager;
import org.appfuse.service.MenuManager;
import org.appfuse.service.UserExistsException;
import org.appfuse.webapp.form.CriteriaForm;

/**
 * Action class to handle CRUD on a Menu object
 * 
 * @struts.action name="criteriaForm" path="/criterias" scope="request"
 *                validate="false" parameter="method" input="mainMenu"
 * @struts.action name="criteriaForm" path="/editMenu" scope="request"
 *                validate="false" parameter="method" input="list"
 * @struts.action name="criteriaForm" path="/saveMenu" scope="request"
 *                validate="true" parameter="method" input="edit"
 * @struts.action-set-property property="cancellable" value="true"
 * @struts.action-forward name="edit" path="/WEB-INF/pages/criteriaForm.jsp"
 * @struts.action-forward name="list" path="/WEB-INF/pages/criteriaList.jsp"
 * @struts.action-forward name="search" path="/criterias.html" redirect="true"
 */
public final class CriteriaAction extends BaseAction {

	public ActionForward cancel(ActionMapping mapping, ActionForm form,
			HttpServletRequest request, HttpServletResponse response)
			throws Exception {
		return mapping.findForward("list");
	}

	public ActionForward delete(ActionMapping mapping, ActionForm form,
			HttpServletRequest request, HttpServletResponse response)
			throws Exception {
		if (log.isDebugEnabled()) {
			log.debug("Entering 'delete' method");
		}

		boolean isNew = false; // flag to denote if menu tree need refresh.
		ActionMessages messages = new ActionMessages();
		CriteriaManager manager = (CriteriaManager) getBean("criteriaManager");
		CriteriaForm criteriaForm = (CriteriaForm) form;
		String[] selectedIds = criteriaForm.getSelectedId();

		// generate criteria list.
		List criterias = (ArrayList) request.getSession().getAttribute(
				Constants.CRITERIA_LIST);
		if (criterias == null) {
			String parentNumber = criteriaForm.getParentNumber();
			Criteria parentCriteria = new Criteria();
			parentCriteria.setCriteriaNumber(parentNumber);
			criterias = manager.getCriterias(parentCriteria);
		}

		for (int i = 0; i < selectedIds.length; i++) {
			String id = selectedIds[i];
			if (id == null || id.length() < 1) {
				continue;
			}
			Criteria criteria = manager.getCriteria(id);
			if (hasChildren(criteria)) {
				messages.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage(
						"criteria.haschildren", criteria.getCriteriaName()));
				continue;
			}
			if (!isLeafCriteria(criteria)) {
				isNew = true;
			}
			criterias.remove(criteria);
			removeFromRepository(criteria, request);
			manager.removeCriteria(String.valueOf(criteria.getCriteriaId()));

		}
		saveMessages(request.getSession(), messages);
		request.getSession().setAttribute(Constants.CRITERIA_LIST, criterias);
		request.setAttribute("isNew", new Boolean(isNew));
		return mapping.findForward("list");
	}

	public ActionForward edit(ActionMapping mapping, ActionForm form,
			HttpServletRequest request, HttpServletResponse response)
			throws Exception {
		if (log.isDebugEnabled()) {
			log.debug("Entering 'edit' method");
		}

		String parentNumber = (String) request.getParameter("parentNumber");
		String selectedId = (String) request.getParameter("selectedId");
	//	System.out.println(" in edit parentNumber:" + parentNumber);
		CriteriaForm criteriaForm = (CriteriaForm) form;

		// if an id is passed in, look up the user - otherwise
		// don't do anything - user is doing an add
		if (criteriaForm.getSelectedId() != null) {
			CriteriaManager mgr = (CriteriaManager) getBean("criteriaManager");
			Criteria criteria = mgr
					.getCriteria(criteriaForm.getSelectedId()[1]);
			criteriaForm = (CriteriaForm) convert(criteria);
			updateFormBean(mapping, request, criteriaForm);
		}
		// request.getSession().setAttribute("parentNumber", parentNumber);
		return mapping.findForward("edit");
	}

	public ActionForward save(ActionMapping mapping, ActionForm form,
			HttpServletRequest request, HttpServletResponse response)
			throws Exception {
		if (log.isDebugEnabled()) {
			log.debug("Entering 'save' method");
		}

		// run validation rules on this form
		// See https://appfuse.dev.java.net/issues/show_bug.cgi?id=128
		ActionMessages errors = form.validate(mapping, request);

		if (!errors.isEmpty()) {
			saveErrors(request, errors);
			return mapping.findForward("add");
		}

		// Extract attributes and parameters we will need
		ActionMessages messages = new ActionMessages();
		CriteriaForm criteriaForm = (CriteriaForm) form;
		boolean isNew = false;

		CriteriaManager mgr = (CriteriaManager) getBean("criteriaManager");
		Criteria criteria = (Criteria) convert(criteriaForm);
		criteria.setParentNumber(removeCriteriaPrefix(criteriaForm
				.getParentNumber()));

		try {
			mgr.saveCriteria(criteria);
		} catch (CriteriaExistsException e) {
			log.warn(e.getMessage());
			errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage(
					"errors.existing.criteria", criteriaForm
							.getCriteriaNumber()));
			saveErrors(request, errors);
			BeanUtils.copyProperties(criteriaForm, convert(criteria));
			// request.setAttribute("parentNumber", criteria.getParentNumber());
			updateFormBean(mapping, request, criteriaForm);
			return mapping.findForward("add");
		}

		// add success messages
		messages.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage(
				"criteria.added"));
		// save messages in session to survive a redirect
		saveMessages(request.getSession(), messages);

		// update criteriaList in session scope
		List criteriaList = (List) request.getSession().getAttribute(
				Constants.CRITERIA_LIST);
		if (criteriaList != null && criteriaList.size() > 0) {
			criteriaList.add(criteria);
		} else {
			Criteria parentCriteria = new Criteria();
			String parentNumber = criteria.getParentNumber();
			parentCriteria.setCriteriaNumber(parentNumber);
			criteriaList = mgr.getCriterias(parentCriteria);
			request.getSession().setAttribute(Constants.CRITERIA_LIST,
					criteriaList);
		}

		// update menu tree when non-leaf criteria is added.
		if (!isLeafCriteria(criteria)) {
			isNew = true;
			addToRepository(criteria, request);
			request.setAttribute("isNew", "true");
		}

		return mapping.findForward("list");
	}

	public ActionForward update(ActionMapping mapping, ActionForm form,
			HttpServletRequest request, HttpServletResponse response)
			throws Exception {
		if (log.isDebugEnabled()) {
			log.debug("Entering 'save' method");
		}

		// run validation rules on this form
		// See https://appfuse.dev.java.net/issues/show_bug.cgi?id=128
		ActionMessages errors = form.validate(mapping, request);

		if (!errors.isEmpty()) {
			saveErrors(request, errors);
			return mapping.findForward("edit");
		}

		// Extract attributes and parameters we will need
		ActionMessages messages = new ActionMessages();
		CriteriaForm criteriaForm = (CriteriaForm) form;
		boolean isNew = false;

		CriteriaManager mgr = (CriteriaManager) getBean("criteriaManager");
		Criteria criteria = (Criteria) convert(criteriaForm);
		criteria.setParentNumber(removeCriteriaPrefix(criteriaForm
				.getParentNumber()));

		mgr.updateCriteria(criteria);

		// update criteriaList in session scope
		List criteriaList = (List) request.getSession().getAttribute(
				Constants.CRITERIA_LIST);
		if (criteriaList != null && criteriaList.size() > 0) {
			int index = criteriaList.indexOf(criteria);
			criteriaList.remove(index);
			criteriaList.add(index, criteria);
		} else {
			Criteria parentCriteria = new Criteria();
			String parentNumber = criteria.getParentNumber();
			parentCriteria.setCriteriaNumber(parentNumber);
			criteriaList = mgr.getCriterias(parentCriteria);
			request.getSession().setAttribute(Constants.CRITERIA_LIST,
					criteriaList);
		}

		// update menu tree when non-leaf criteria is update.
		if (!isLeafCriteria(criteria)) {
			isNew = updateRepository(criteria, request);
		}

		// request.setAttribute("parentNumber", parentNumber);
		request.setAttribute("isNew", new Boolean(isNew));
		// request.getSession().setAttribute(Constants.CRITERIA_LIST,
		// criterias);

		messages.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage(
				"criteria.updated"));
		saveMessages(request, messages);

		return mapping.findForward("list");
	}

	public ActionForward list(ActionMapping mapping, ActionForm form,
			HttpServletRequest request, HttpServletResponse response)
			throws Exception {
		if (log.isDebugEnabled()) {
			log.debug("Entering 'list' method");
		}

		
		  String name = request.getParameter("name"); name =
		  removeCriteriaPrefix(name); System.out.println("name : " + name);
		  CriteriaForm criteriaForm = (CriteriaForm) form; Criteria criteria =
		  (Criteria) convert(criteriaForm); criteria.setCriteriaNumber(name);
		  CriteriaManager mgr = (CriteriaManager) getBean("criteriaManager");
		  List criteriaList = mgr.getCriterias(criteria);
		  request.getSession().setAttribute(Constants.CRITERIA_LIST,
		  criteriaList);
		 
		// MenuRepository repository = populateRepository(criteriaList,
		// request);
		
		  request.getSession().setAttribute("parentNumber",
		  addCriteriaPrefix(name));
		 
		// request.setAttribute("repository", repository);
		// request.getSession().setAttribute("repository", repository);
		return mapping.findForward("list");
	}

	public ActionForward getSubCriteria(ActionMapping mapping, ActionForm form,
			HttpServletRequest request, HttpServletResponse response)
			throws Exception {
		if (log.isDebugEnabled()) {
			log.debug("Entering 'search' method");
		}

		CriteriaForm criteriaForm = (CriteriaForm) form;
		Criteria criteria = (Criteria) convert(criteriaForm);

		String name = request.getParameter("name");
		name = removeCriteriaPrefix(name);

		// add criteria items to list
		if (name.equals(Constants.DEFAULT_TOP_MENU_NAME)) {
			name = null;
		}
		Criteria m = new Criteria();
		m.setCriteriaNumber(name);
		CriteriaManager mgr = (CriteriaManager) getBean("criteriaManager");
		List criterias = mgr.getCriterias(m);

		// criterias.addAll(menus);
		request.getSession().removeAttribute("parentNumber");
		request.getSession().setAttribute("parentNumber",
				addCriteriaPrefix(name));
		request.getSession().removeAttribute(Constants.CRITERIA_LIST);
		request.getSession().setAttribute(Constants.CRITERIA_LIST, criterias);
		return mapping.findForward("subCriteriaList");
	}

	public ActionForward unspecified(ActionMapping mapping, ActionForm form,
			HttpServletRequest request, HttpServletResponse response)
			throws Exception {
		return list(mapping, form, request, response);
	}

	protected void addToRepository(Criteria criteria, HttpServletRequest request) {

		// dynamic criteria start
		// Get the repository from the application scope - and copy the
		// DisplayerMappings from it.
		MenuRepository repository = (MenuRepository) request.getSession()
				.getServletContext()
				.getAttribute(Constants.MENU_REPOSITORY_KEY);

		MenuComponent mc = new MenuComponent();
		String criteriaNumber = criteria.getCriteriaNumber();
		criteriaNumber = addCriteriaPrefix(criteriaNumber);
		mc.setName(criteriaNumber);

		String path = new String(request.getRequestURL());
		// String location = m.getLocation();
		String location = Constants.DEFAULT_LOCATION;
		int index = request.getRequestURL().lastIndexOf("/");
		path = path.substring(0, index);
		location = path + "/" + location;
		mc.setLocation(location);

		String title = criteria.getTitle();
		mc.setTitle(title);

		String parentNumber = criteria.getParentNumber();
		if (parentNumber != null) {
			parentNumber = addCriteriaPrefix(parentNumber);
			MenuComponent parentMenu = repository.getMenu(parentNumber);
			if (parentMenu == null) {
				System.out.println("parentMenu '" + parentNumber
						+ "' doesn't exist!");
				parentMenu = new MenuComponent();
				parentMenu.setName(parentNumber);
				parentMenu.setLocation(location);
				repository.addMenu(parentMenu);
			} else {
				parentMenu.setLocation(location);
			}

			mc.setParent(parentMenu);
		} else {
			MenuComponent parentMenu = repository
					.getMenu(Constants.DEFAULT_TOP_MENU_NAME);
			if (parentMenu == null) {
				parentMenu = new MenuComponent();
				parentMenu.setName(parentNumber);
				repository.addMenu(parentMenu);
			}
			mc.setParent(parentMenu);
		}

		repository.addMenu(mc);
	}

	protected void removeFromRepository(Criteria criteria,
			HttpServletRequest request) {

		// dynamic criteria start
		// Get the repository from the application scope - and copy the
		// DisplayerMappings from it.
		MenuRepository repository = (MenuRepository) request.getSession()
				.getServletContext()
				.getAttribute(Constants.MENU_REPOSITORY_KEY);

		String criteriaNumber = criteria.getCriteriaNumber();
		criteriaNumber = addCriteriaPrefix(criteriaNumber);
		repository.removeMenu(criteriaNumber);
	}

	protected boolean updateRepository(Criteria criteria,
			HttpServletRequest request) {

		// dynamic criteria start
		// Get the repository from the application scope - and copy the
		// DisplayerMappings from it.
		MenuRepository repository = (MenuRepository) request.getSession()
				.getServletContext()
				.getAttribute(Constants.MENU_REPOSITORY_KEY);

		String criteriaNumber = criteria.getCriteriaNumber();
		criteriaNumber = addCriteriaPrefix(criteriaNumber);
		MenuComponent mc = repository.getMenu(criteriaNumber);

		String title = criteria.getTitle();
		if (mc.getTitle().equals(title)) {
			return false;
		}
		mc.setTitle(title);
		return true;
	}

	protected String addCriteriaPrefix(String criteriaNumber) {
		if (StringUtils.isBlank(criteriaNumber)
				|| criteriaNumber.startsWith(Constants.CRITERIA_NAME_PREFIX)) {
			return criteriaNumber;
		}
		if (!hasCriteriaPrefix(criteriaNumber)) {
			return Constants.CRITERIA_NAME_PREFIX + criteriaNumber;
		} else {
			return criteriaNumber;
		}
	}

	protected String removeCriteriaPrefix(String criteriaNumber) {
		if (StringUtils.isBlank(criteriaNumber)
				|| !criteriaNumber.startsWith(Constants.CRITERIA_NAME_PREFIX)) {
			return criteriaNumber;
		}
		if (hasCriteriaPrefix(criteriaNumber)) {
			return StringUtils.remove(criteriaNumber,
					Constants.CRITERIA_NAME_PREFIX);
		}
		return criteriaNumber;
	}

	protected boolean hasCriteriaPrefix(String number) {
		return number.startsWith(Constants.CRITERIA_NAME_PREFIX);
	}

	protected boolean isTopCriteria(Criteria criteria) {
		if (criteria == null) {
			return false;
		}

		return criteria.getCriteriaNumber().endsWith("000000");
	}

	protected boolean isLeafCriteria(Criteria criteria) {
		if (criteria == null || criteria.getCriteriaNumber() == null) {
			return true;
		}

		return !criteria.getCriteriaNumber().endsWith("00");
	}

	protected boolean isLeafCriteria(CriteriaForm criteriaForm) {
		if (criteriaForm == null || criteriaForm.getCriteriaNumber() == null) {
			return true;
		}
		return !criteriaForm.getCriteriaNumber().endsWith("00");
	}

	protected boolean hasChildren(Criteria criteria) {
		CriteriaManager manager = (CriteriaManager) getBean("criteriaManager");
		List children = manager.getCriterias(criteria);
		return (children != null && !children.isEmpty());
	}

}
